package com.moust.cordova.videoplayer; import android.annotation.TargetApi; import android.app.Dialog; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnDismissListener; import android.content.res.AssetFileDescriptor; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; import android.net.Uri; import android.os.Build; import android.util.Log; import android.view.Gravity; import android.view.SurfaceHolder; import android.view.Window; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.widget.LinearLayout; import android.widget.VideoView; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaArgs; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CordovaResourceApi; import org.apache.cordova.PluginResult; import org.json.JSONException; import org.json.JSONObject; public class VideoPlayer extends CordovaPlugin implements OnCompletionListener, OnPreparedListener, OnErrorListener, OnDismissListener { protected static final String LOG_TAG = "VideoPlayer"; protected static final String ASSETS = "/android_asset/"; private CallbackContext callbackContext = null; private Dialog dialog; private VideoView videoView; private MediaPlayer player; /** * Executes the request and returns PluginResult. * * @param action The action to execute. * @param args JSONArray of arguments for the plugin. * @param callbackId The callback id used when calling back into JavaScript. * @return A PluginResult object with a status and message. */ public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException { if (action.equals("play")) { this.callbackContext = callbackContext; CordovaResourceApi resourceApi = webView.getResourceApi(); String target = args.getString(0); final JSONObject options = args.getJSONObject(1); String fileUriStr; try { Uri targetUri = resourceApi.remapUri(Uri.parse(target)); fileUriStr = targetUri.toString(); } catch (IllegalArgumentException e) { fileUriStr = target; } Log.v(LOG_TAG, fileUriStr); final String path = stripFileProtocol(fileUriStr); // Create dialog in new thread cordova.getActivity().runOnUiThread(new Runnable() { public void run() { openVideoDialog(path, options); } }); // Don't return any result now PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT); pluginResult.setKeepCallback(true); callbackContext.sendPluginResult(pluginResult); callbackContext = null; return true; } else if (action.equals("close")) { if (dialog != null) { if(player.isPlaying()) { player.stop(); } player.release(); dialog.dismiss(); } if (callbackContext != null) { PluginResult result = new PluginResult(PluginResult.Status.OK); result.setKeepCallback(false); // release status callback in JS side callbackContext.sendPluginResult(result); callbackContext = null; } return true; } return false; } /** * Removes the "file://" prefix from the given URI string, if applicable. * If the given URI string doesn't have a "file://" prefix, it is returned unchanged. * * @param uriString the URI string to operate on * @return a path without the "file://" prefix */ public static String stripFileProtocol(String uriString) { if (uriString.startsWith("file://")) { return Uri.parse(uriString).getPath(); } return uriString; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) protected void openVideoDialog(String path, JSONObject options) { // Let's create the main dialog dialog = new Dialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar); dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog; dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setCancelable(true); dialog.setOnDismissListener(this); // Main container layout LinearLayout main = new LinearLayout(cordova.getActivity()); main.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); main.setOrientation(LinearLayout.VERTICAL); main.setHorizontalGravity(Gravity.CENTER_HORIZONTAL); main.setVerticalGravity(Gravity.CENTER_VERTICAL); videoView = new VideoView(cordova.getActivity()); videoView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // videoView.setVideoURI(uri); // videoView.setVideoPath(path); main.addView(videoView); player = new MediaPlayer(); player.setOnPreparedListener(this); player.setOnCompletionListener(this); player.setOnErrorListener(this); if (path.startsWith(ASSETS)) { String f = path.substring(15); AssetFileDescriptor fd = null; try { fd = cordova.getActivity().getAssets().openFd(f); player.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); } catch (Exception e) { PluginResult result = new PluginResult(PluginResult.Status.ERROR, e.getLocalizedMessage()); result.setKeepCallback(false); // release status callback in JS side callbackContext.sendPluginResult(result); callbackContext = null; return; } } else { try { player.setDataSource(path); } catch (Exception e) { PluginResult result = new PluginResult(PluginResult.Status.ERROR, e.getLocalizedMessage()); result.setKeepCallback(false); // release status callback in JS side callbackContext.sendPluginResult(result); callbackContext = null; return; } } try { float volume = Float.valueOf(options.getString("volume")); Log.d(LOG_TAG, "setVolume: " + volume); player.setVolume(volume, volume); } catch (Exception e) { PluginResult result = new PluginResult(PluginResult.Status.ERROR, e.getLocalizedMessage()); result.setKeepCallback(false); // release status callback in JS side callbackContext.sendPluginResult(result); callbackContext = null; return; } if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { try { int scalingMode = options.getInt("scalingMode"); switch (scalingMode) { case MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING: Log.d(LOG_TAG, "setVideoScalingMode VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING"); player.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); break; default: Log.d(LOG_TAG, "setVideoScalingMode VIDEO_SCALING_MODE_SCALE_TO_FIT"); player.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT); } } catch (Exception e) { PluginResult result = new PluginResult(PluginResult.Status.ERROR, e.getLocalizedMessage()); result.setKeepCallback(false); // release status callback in JS side callbackContext.sendPluginResult(result); callbackContext = null; return; } } final SurfaceHolder mHolder = videoView.getHolder(); mHolder.setKeepScreenOn(true); mHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { player.setDisplay(holder); try { player.prepare(); } catch (Exception e) { PluginResult result = new PluginResult(PluginResult.Status.ERROR, e.getLocalizedMessage()); result.setKeepCallback(false); // release status callback in JS side callbackContext.sendPluginResult(result); callbackContext = null; } } @Override public void surfaceDestroyed(SurfaceHolder holder) { player.release(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} }); WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); lp.copyFrom(dialog.getWindow().getAttributes()); lp.width = WindowManager.LayoutParams.MATCH_PARENT; lp.height = WindowManager.LayoutParams.MATCH_PARENT; dialog.setContentView(main); dialog.show(); dialog.getWindow().setAttributes(lp); } @Override public boolean onError(MediaPlayer mp, int what, int extra) { Log.e(LOG_TAG, "MediaPlayer.onError(" + what + ", " + extra + ")"); if(mp.isPlaying()) { mp.stop(); } mp.release(); dialog.dismiss(); return false; } @Override public void onPrepared(MediaPlayer mp) { mp.start(); } @Override public void onCompletion(MediaPlayer mp) { Log.d(LOG_TAG, "MediaPlayer completed"); mp.release(); dialog.dismiss(); } @Override public void onDismiss(DialogInterface dialog) { Log.d(LOG_TAG, "Dialog dismissed"); if (callbackContext != null) { PluginResult result = new PluginResult(PluginResult.Status.OK); result.setKeepCallback(false); // release status callback in JS side callbackContext.sendPluginResult(result); callbackContext = null; } } }